package handler

import (
	"context"
	"github.com/golang/protobuf/ptypes/empty"
	"github.com/golang/protobuf/ptypes/wrappers"

	"kop/framework/broker"
	context2 "kop/framework/context"
	"kop/framework/errors"
	"kop/libs/database"
	"kop/modules/building/configuration"
	"kop/modules/building/entity"
	"kop/modules/building/models"
	"kop/modules/captain/configuration/fetter"
	"kop/modules/role/configuration/level"
	"kop/pb"
	"kop/pb/service"

	captainC "kop/modules/captain/client"
	guildC "kop/modules/guild/client"
	itemC "kop/modules/item/client"
	moneyC "kop/modules/money/client"
	questC "kop/modules/quest/client"
	roleC "kop/modules/role/client"
)

type BuildingServiceHandler struct {
	context2.Context
}

func (h *BuildingServiceHandler) GetBuildings(_ context.Context, req *service.RoleRequest, resp *service.Buildings) error {

	resp.Buildings = models.GetBuildings(database.DB, req.GetRoleID())
	return nil
}

func (h *BuildingServiceHandler) GetBuilding(_ context.Context, req *service.DefaultRequest, resp *service.Building) error {

	_ = configuration.GetByBuildingID(req.Value)

	var building, ok = models.GetByBuildingID(database.DB, req.RoleID, req.Value)
	if ok {
		resp.Building = building
	}
	return nil
}

func (h *BuildingServiceHandler) Create(ctx context.Context, req *wrappers.Int32Value, resp *pb.Building) error {

	var roleID, buildingID = h.GetRoleID(ctx), req.Value

	var attrConf = configuration.GetByBuildingID(buildingID).GetAttrByLevel(0)

	if err := moneyC.AsyncSubCoin(ctx, attrConf.MoneyCost); err != nil {
		return err
	}

	hall, _ := models.GetByBuildingID(database.DB, roleID, entity.HALL)
	*resp = *models.Create(database.DB, roleID, buildingID, newBuildingCoin(roleC.GetRole(roleID).Level, buildingID, hall.Level))

	h.updateIncome(roleID, hall)
	questC.Ship(roleID, buildingID)
	return nil
}

func (h *BuildingServiceHandler) HallLevelUp(ctx context.Context, _ *empty.Empty, resp *pb.HallLevelUpReply) error {

	var roleID = h.GetRoleID(ctx)

	var hall, _ = models.GetByBuildingID(database.DB, roleID, entity.HALL)

	var itemID int32 = 101001 // 101001
	var prepare, err = itemC.NewPrepare(h.GetRoleID(ctx), h.GetServerID(ctx)).Prepare(itemID, hall.ItemCost)
	if err != nil {
		return err
	}

	if resp.Money, err = moneyC.SubCoin(ctx, hall.MoneyCost); err != nil {
		return err
	}

	resp.Item, err = prepare.Run()
	if err != nil {
		return err
	}

	hall.Level += 1
	hall.Coin = configuration.GetByBuildingID(entity.HALL).GetAttrByLevel(hall.Level).Income()
	models.SaveBuilding(database.DB, hall)

	resp.Buildings = []*pb.Building{hall}
	return h.setAllIncome(roleID)
}

func (h *BuildingServiceHandler) LevelUp(ctx context.Context, req *wrappers.Int32Value, resp *pb.BuildingLevelUpReply) error {

	var buildingID = req.Value
	if buildingID == 0 || buildingID == entity.HALL {
		return errors.ParamError
	}

	var roleID = h.GetRoleID(ctx)
	var building, ok = models.GetByBuildingID(database.DB, roleID, buildingID)
	if !ok {
		return errors.ParamError
	}

	if building.Seaman < building.SailorCost {
		// 检查水手数量
		return errors.New("seaman not enough")
	}

	var itemID int32 = 101002 // 配的是101002
	var prepare, err = itemC.NewPrepare(h.GetRoleID(ctx), h.GetServerID(ctx)).Prepare(itemID, building.ItemCost)
	if err != nil {
		return err
	}

	if resp.Money, err = moneyC.SubCoin(ctx, building.MoneyCost); err != nil {
		return err
	}

	resp.Item, err = prepare.Run()
	if err != nil {
		return err
	}

	var hall, _ = models.GetByBuildingID(database.DB, roleID, entity.HALL)

	building.Level += 1
	building.Coin = h.countCoin(building, nil, hall, nil)
	models.SaveBuilding(database.DB, building)

	if building.IsShip() {
		questC.ShipLevelUp(roleID)
	}

	resp.Building = building

	h.updateIncome(roleID, hall)
	return nil
}

//
//// 建筑升级
//func (b *BuildingServiceHandler) LevelUp(ctx context.Context, req *service.DefaultRequest, building *pb.Building) error {
//
//	var err = b.GetBuilding(ctx, req, building)
//	if err != nil {
//		return err
//	}
//
//	building.Level += 1
//
//	var hall *pb.Building = nil
//	if building.BuildingID == entity.HALL {
//		hall = building
//	}
//
//	// 重新计算收益
//	if err = countCoin(building, hall, nil); err != nil {
//		return err
//	} else if err = models.SaveBuild(building); err != nil {
//		return err
//	}
//
//	updateIncome(req.RoleID)
//	return nil
//}

func (h *BuildingServiceHandler) AddSeaman(ctx context.Context, req *pb.AddSeamanRequest, resp *pb.AddSeamanResp) error {

	if req.BuildingID == 0 {
		return errors.ParamError
	}

	if req.Seaman < 1 || req.Seaman > 10 {
		return errors.ParamError
	}

	var roleID = h.GetRoleID(ctx)

	var building, _ = models.GetByBuildingID(database.DB, roleID, req.BuildingID)

	if building.Level < 1 {
		return errors.ParamError
	}

	if configuration.GetMoneyBySailorNumber(building.Seaman+1) == 0 {
		return errors.ParamError
	}

	var role = roleC.GetRole(roleID)
	roleLevelConf, err := level.GetByRoleLevel(role.Level)
	if err != nil {
		return err
	}

	if building.Seaman >= roleLevelConf.SailorMax {
		return errors.ParamError
	}

	var coin int64 = 0
	var seaman int32 = 0

	for i := int32(1); i <= req.Seaman; i++ {
		var c = configuration.GetMoneyBySailorNumber(building.Seaman + i)
		if c == 0 {
			break
		}

		if building.Seaman+i > roleLevelConf.SailorMax {
			break
		}

		coin += c
		seaman++
	}

	// ----------------------------------------- 公会技能 ----------------------------------------- //
	if h.GetGuildID(ctx) > 0 {
		skillValue, err := guildC.GuildService.GetSkillByShipID(ctx, &wrappers.Int32Value{Value: building.BuildingID})
		if err != nil {
			return err
		}
		coin = coin - int64(float64(coin)*float64(skillValue.Value))
	}
	// ----------------------------------------- 公会技能 ----------------------------------------- //

	if resp.Money, err = moneyC.SubCoin(ctx, coin); err != nil {
		return err
	}

	building.Seaman += seaman
	building.Coin = h.countCoin(building, role, nil, nil)
	models.SaveBuilding(database.DB, building)

	// 主线任务
	questC.Sailor(roleID, building.Seaman, building.BuildingID)
	//questC.Employ(roleID, seaman)
	go questC.DailyQuestEmploy(roleID, seaman)

	h.updateIncome(roleID, nil)
	resp.Building = building
	return nil
}

func (h *BuildingServiceHandler) Save(ctx context.Context, req *pb.Building, building *pb.Building) error {

	*building = *req
	building.Coin = h.countCoin(building, nil, nil, nil)

	models.SaveBuilding(database.DB, building)

	h.updateIncome(req.RoleID, nil)
	return nil
}

// 更新船长信息重新计算建筑收益
func (h *BuildingServiceHandler) ResetCoin(ctx context.Context, req *service.ResetCoinRequest, resp *pb.Building) error {

	var roleID = h.GetRoleID(ctx)
	var building, ok = models.GetByBuildingID(database.DB, roleID, req.ShipID)
	if !ok {
		return errors.ParamError
	}

	var hall, _ = models.GetByBuildingID(database.DB, roleID, entity.HALL)

	if coin := h.countCoin(building, nil, hall, req.Captains); coin != building.Coin {
		building.Coin = coin
		models.SaveBuilding(database.DB, building)

		h.updateIncome(roleID, hall)
		_ = broker.RolePublish(roleID, pb.WsCode_CodeBuilding, building)
	}

	*resp = *building
	return nil
}

// 主城升级后重新计算所有建筑收益
func (h *BuildingServiceHandler) setAllIncome(roleID int32) error {

	var buildings = models.GetBuildings(database.DB, roleID)

	var hall *pb.Building
	for _, building := range buildings {
		if building.BuildingID == entity.HALL {
			hall = building
		}
	}

	var captains = captainC.GetCaptains(roleID)
	var role = roleC.GetRole(roleID)

	for _, building := range buildings {

		if building.BuildingID != entity.HALL {

			var b = *building
			b.Coin = h.countCoin(building, role, hall, captains)

			models.SaveBuilding(database.DB, &b)
			_ = broker.RolePublish(roleID, pb.WsCode_CodeBuilding, &b)

		}

	}

	h.updateIncome(roleID, hall)
	return nil
}

// 计算建筑每秒银币收益
// hall 默认传nil
// captains 默认传nil
func (h *BuildingServiceHandler) countCoin(building *pb.Building, role *pb.Role, hall *pb.Building, captains []*pb.Captain) int64 {

	var buildingAttrConf = configuration.GetByBuildingID(building.BuildingID).GetAttrByLevel(building.Level)

	if building.BuildingID == entity.HALL {
		return buildingAttrConf.Income()
	}

	if role == nil {
		role = roleC.GetRole(building.RoleID)
	}

	var roleLevelConf, _ = level.GetByRoleLevel(role.Level)

	if captains == nil {
		captains = captainC.GetCaptains(building.RoleID)
	}

	var fetterValue float64 = 0

	var chr int32 = 0
	for _, captain := range captains {

		if captain.BuildingID > 0 {

			fetterValue += fetter.GetFetter(captain.Fetter1.ID).MoneyRate

			if captain.BuildingID == building.BuildingID {

				chr += captain.Attribute.CHR
				if captain.Fetter1 != nil {
					fetterValue += fetter.GetFetter(captain.Fetter1.ID).MoneyRateEX
				}
			}
		}
	}

	if hall == nil {
		hall, _ = models.GetByBuildingID(database.DB, building.RoleID, entity.HALL)
	}

	var hallAttrConf = configuration.GetByBuildingID(entity.HALL).GetAttrByLevel(hall.Level)

	return int64(float64(int64(chr)+buildingAttrConf.MoneyIncome+int64(building.Seaman)*5) * (buildingAttrConf.MoneyRate + hallAttrConf.MoneyRateAll) * (1 + roleLevelConf.MoneyRateAll) * (1 + fetterValue))
}

func newBuildingCoin(roleLevel, buildingID, hallLevel int32) int64 {

	var hallAttrConf = configuration.GetByBuildingID(entity.HALL).GetAttrByLevel(hallLevel)
	var buildingAttrConf = configuration.GetByBuildingID(buildingID).GetAttrByLevel(1)

	var roleLevelConf, _ = level.GetByRoleLevel(roleLevel)

	//每秒银币收益=(海盗船基础收益)*(海盗船等级加成+海盗大厅等级加成)*(1+船长技能加成+海盗称号加成)
	return int64(float64(buildingAttrConf.MoneyIncome) * (buildingAttrConf.MoneyRate + hallAttrConf.MoneyRateAll) * (1 + roleLevelConf.MoneyRateAll))
}

func (h *BuildingServiceHandler) updateIncome(roleID int32, hall *pb.Building) {

	if hall == nil {
		hall, _ = models.GetByBuildingID(database.DB, roleID, entity.HALL)
	}

	go func() {

		var coin, err = models.Income(database.DB, roleID)
		if hall.Level >= 4 {
			coin += hall.Coin
		}

		if err == nil && coin > 0 {
			err = moneyC.UpdateIncome(roleID, coin)

			questC.Income(roleID, coin)
		}
	}()
}
